package com.example.points.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.points.domain.PointsDetail;
import com.example.points.domain.UserPoints;
import com.example.points.mapper.PointsDetailMapper;
import com.example.points.mapper.UserPointsMapper;
import com.example.points.service.PointsOperationLogService;
import com.example.points.service.UserPointsService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
@RequiredArgsConstructor
public class UserPointsServiceImpl implements UserPointsService {
    private final UserPointsMapper userPointsMapper;
    private final PointsDetailMapper pointsDetailMapper;
    private final PointsOperationLogService operationLogService;
    private final RedisTemplate<String, Object> redisTemplate;

    private static final String POINTS_CACHE_KEY = "user:points:";
    private static final long CACHE_EXPIRE_TIME = 30;

    @Override
    public UserPoints getUserPoints(Long userId) {
        String cacheKey = POINTS_CACHE_KEY + userId;
        UserPoints userPoints = (UserPoints) redisTemplate.opsForValue().get(cacheKey);
        if (userPoints != null) {
            return userPoints;
        }

        userPoints = userPointsMapper.selectOne(
            new LambdaQueryWrapper<UserPoints>().eq(UserPoints::getUserId, userId)
        );
        
        if (userPoints != null) {
            redisTemplate.opsForValue().set(cacheKey, userPoints, CACHE_EXPIRE_TIME, TimeUnit.MINUTES);
        }
        
        return userPoints;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean rechargePoints(Long userId, Long points, String description) {
        UserPoints userPoints = getUserPoints(userId);
        if (userPoints == null) {
            userPoints = new UserPoints();
            userPoints.setUserId(userId);
            userPoints.setTotalPoints(0L);
            userPoints.setAvailablePoints(0L);
            userPoints.setFrozenPoints(0L);
            userPoints.setVersion(0);
            userPointsMapper.insert(userPoints);
        }

        Long beforePoints = userPoints.getAvailablePoints();
        boolean success = userPointsMapper.updatePoints(userId, points, userPoints.getVersion()) > 0;
        
        if (success) {
            // Record detail
            PointsDetail detail = new PointsDetail();
            detail.setUserId(userId);
            detail.setPoints(points);
            detail.setType(1); // Recharge
            detail.setStatus(1); // Valid
            detail.setExpireTime(LocalDateTime.now().plusYears(1));
            detail.setDescription(description);
            pointsDetailMapper.insert(detail);

            // Record operation log
            operationLogService.recordOperation(userId, 1, points, beforePoints, 
                beforePoints + points, "SYSTEM", description);

            // Clear cache
            redisTemplate.delete(POINTS_CACHE_KEY + userId);
        }

        return success;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean consumePoints(Long userId, Long points, String description) {
        UserPoints userPoints = getUserPoints(userId);
        if (userPoints == null || userPoints.getAvailablePoints() < points) {
            return false;
        }

        Long beforePoints = userPoints.getAvailablePoints();
        boolean success = userPointsMapper.updatePoints(userId, -points, userPoints.getVersion()) > 0;
        
        if (success) {
            // Record detail
            PointsDetail detail = new PointsDetail();
            detail.setUserId(userId);
            detail.setPoints(-points);
            detail.setType(2); // Consume
            detail.setStatus(1); // Valid
            detail.setDescription(description);
            pointsDetailMapper.insert(detail);

            // Record operation log
            operationLogService.recordOperation(userId, 2, points, beforePoints, 
                beforePoints - points, "SYSTEM", description);

            // Clear cache
            redisTemplate.delete(POINTS_CACHE_KEY + userId);
        }

        return success;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean batchRechargePoints(List<Long> userIds, Long points, String description) {
        for (Long userId : userIds) {
            try {
                rechargePoints(userId, points, description);
            } catch (Exception e) {
                // Log error but continue processing
                continue;
            }
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean batchConsumePoints(List<Long> userIds, Long points, String description) {
        for (Long userId : userIds) {
            try {
                consumePoints(userId, points, description);
            } catch (Exception e) {
                // Log error but continue processing
                continue;
            }
        }
        return true;
    }

    @Override
    @PostConstruct
    @Scheduled(cron = "0 0 1 * * ?") // Run at 1 AM every day
    public void checkExpiringPoints() {
        LocalDateTime expirationTime = LocalDateTime.now().plusDays(7); // Check points expiring in 7 days
        List<PointsDetail> expiringPoints = pointsDetailMapper.findExpiringPoints(null, expirationTime);
        
        for (PointsDetail detail : expiringPoints) {
            String message = String.format("User %d has %d points expiring at %s", 
                detail.getUserId(), detail.getPoints(), detail.getExpireTime());
            redisTemplate.opsForList().rightPush("points:expiring:notifications", message);
        }
    }
} 